#pragma once

// 代码编译模块
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "../comm/util.hpp"
#include "../comm/log.hpp"

namespace ns_compiler
{
    using namespace ns_util;
    using namespace ns_log;

    class Compiler
    {
    public:
        Compiler() {}

        ~Compiler() {}

        // 编译文件
        // file_name：待编译的文件名
        // 返回值：编译是否成功
        static bool Compile(const std::string file_name)
        {
            // 创建子进程
            pid_t pid = fork();
            if (pid < 0) // 创建子进程失败
            {
                LOG(ERROR) << " 编译模块: 创建子进程失败" << std::endl;
                return false;
            }
            else if (pid == 0)
            {
                // 子进程
                // 提前构建存储错误信息的stderr临时文件
                umask(0); // 清零权限掩码保证stderr文件权限正确设置
                int _stderr = open(PathUtil::CompileError(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
                if (_stderr < 0) // 文件创建失败
                {
                    LOG(WARNING) << " 编译模块: CompileError文件创建失败" << std::endl;
                    exit(1);
                }

                // 将标准错误流重定向到stderr文件中
                dup2(_stderr, 2);

                // 调用编译器完成编译工作
                // g++ -o target src -std=c++11
                // 进程替换，将子进程替换为g++程序
                execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(), PathUtil::Src(file_name).c_str(), "-D", "CompileOnline", "-std=c++11", nullptr); // 别忘nullptr

                LOG(ERROR) << " 编译模块: g++编译器启动失败" << std::endl; // 走到这里说明进程替换不成功
                exit(2);                                                  // 直接退出子进程
            }
            else
            {
                waitpid(pid, nullptr, 0);
                // 判断编译是否成功：可执行文件是否已生成
                if (FileUtil::IsFileExist(PathUtil::Exe(file_name)))
                {
                    LOG(INFO) << " 编译模块: " << PathUtil::Src(file_name) << "编译成功，可执行文件已生成" << std::endl;
                    return true; // 编译成功
                }
            }
            LOG(DEBUG) << PathUtil::Src(file_name) << std::endl;
            LOG(ERROR) << " 编译模块: 编译失败，未生成可执行文件" << std::endl;
            return false; // 失败
        }
    };
}